package com.sino.config.server.controller.config;

import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

import org.nutz.dao.Cnd;
import org.nutz.http.Http;
import org.nutz.ioc.impl.PropertiesProxy;
import org.nutz.lang.ContinueLoop;
import org.nutz.lang.Each;
import org.nutz.lang.ExitLoop;
import org.nutz.lang.Lang;
import org.nutz.lang.LoopException;
import org.nutz.lang.Strings;
import org.nutz.lang.stream.StringWriter;
import org.nutz.lang.util.NutMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.yaml.snakeyaml.Yaml;

import com.sino.config.server.bean.acl.User;
import com.sino.config.server.bean.config.Property;
import com.sino.config.server.bean.project.Application;
import com.sino.config.server.bean.project.ApplicationMember;
import com.sino.config.server.controller.BaseController;
import com.sino.config.server.service.acl.UserService;
import com.sino.config.server.service.config.PropertyService;
import com.sino.config.server.service.project.ApplicationMemberService;
import com.sino.config.server.service.project.ApplicationService;
import com.sino.config.server.service.project.LabelService;
import com.sino.config.server.service.project.ProfileService;

import club.zhcs.common.Result;
import io.swagger.annotations.Api;

/**
 * @author kerbores
 *
 */
@RestController
@RequestMapping("property")
@Api(value = "Property", tags = {"Property"})
public class PropertyController extends BaseController {

    @Autowired
    PropertyService propertyService;

    @Autowired
    ApplicationService applicationService;

    @Autowired
    UserService userService;

    @Autowired
    ProfileService profileService;

    @Autowired
    LabelService labelService;

    @Autowired
    ApplicationMemberService applicationMemberService;

    /**
     * 
     * @param property
     * @return
     */
    @PostMapping("add")
    public Result add(@RequestBody Property property) {
        return propertyService.save(property) != null ? Result.success() : Result.fail("添加配置失败!");
    }

    /**
     * 
     * @param property
     * @return
     */
    @PostMapping("edit")
    public Result edit(@RequestBody Property property) {
        return propertyService.update(property, "key", "value") ? Result.success() : Result.fail("更新配置失败!");
    }

    protected final Map<String, Object> getFlattenedMap(Map<String, Object> source) {
        Map<String, Object> result = new LinkedHashMap<String, Object>();
        buildFlattenedMap(result, source, null);
        return result;
    }

    private void buildFlattenedMap(Map<String, Object> result, Map<String, Object> source, String path) {
        for (Entry<String, Object> entry : source.entrySet()) {
            String key = entry.getKey();
            if (StringUtils.hasText(path)) {
                if (key.startsWith("[")) {
                    key = path + key;
                } else {
                    key = path + '.' + key;
                }
            }
            Object value = entry.getValue();
            if (value instanceof String) {
                result.put(key, value);
            } else if (value instanceof Map) {
                // Need a compound key
                Map<String, Object> map = (Map<String, Object>) value;
                buildFlattenedMap(result, map, key);
            } else if (value instanceof Collection) {
                // Need a compound key
                Collection<Object> collection = (Collection<Object>) value;
                int count = 0;
                for (Object object : collection) {
                    buildFlattenedMap(result,
                                      Collections.singletonMap("[" + (count++) + "]", object),
                                      key);
                }
            } else {
                result.put(key, (value != null ? value : ""));
            }
        }
    }

    /**
     * 
     * @param applicationId
     * @param profileId
     * @param labelId
     * @return
     * @throws IOException
     */
    @GetMapping("{application}/{profile}/{label}/preview")
    public Result preview(@PathVariable("application") long applicationId, @PathVariable("profile") long profileId, @PathVariable("label") long labelId) throws IOException {
        Properties properties = new Properties();
        NutMap data = propertyService.show(applicationId, profileId, labelId);
        properties.putAll(data);
        StringBuilder propertiesStringBuilder = new StringBuilder();
        properties.store(new StringWriter(propertiesStringBuilder), null);
        return Result.success().addData("properties", Http.post("http://toyaml.com/y", NutMap.NEW().addv("prop", propertiesStringBuilder.toString()), 5000));
    }

    /**
     * 导入配置
     * 
     * @param applicationId
     * @param profileId
     * @param labelId
     * @param file
     * @return
     * @throws IOException
     */
    @PostMapping("{application}/{profile}/{label}/import")
    public Result importFromFile(@PathVariable("application") long applicationId, @PathVariable("profile") long profileId, @PathVariable("label") long labelId, MultipartFile file)
            throws IOException {
        NutMap data = null;
        String[] infos = file.getOriginalFilename().split("\\.");
        if (Strings.equalsIgnoreCase(infos[infos.length - 1], "properties")) {// properties处理
            PropertiesProxy proxy = new PropertiesProxy();
            proxy.load(new InputStreamReader(file.getInputStream()));
            data = Lang.obj2nutmap(proxy.toMap());
        } else {// yml处理
            Yaml yaml = new Yaml();
            data = Lang.obj2nutmap(getFlattenedMap(yaml.loadAs(file.getInputStream(), Map.class)));
        }
        Lang.each(data, new Each<Entry<String, Object>>() {

            @Override
            public void invoke(int index, Entry<String, Object> ele, int length) throws ExitLoop, ContinueLoop, LoopException {

                Property temp = propertyService
                                               .fetch(Cnd.where("applicationId", "=", applicationId)
                                                         .and("labelId", "=", labelId)
                                                         .and("profileId", "=", profileId)
                                                         .and("key", "=", ele.getKey()));
                if (temp == null) {
                    temp = new Property();
                    temp.setApplicationId(applicationId);
                    temp.setLabelId(labelId);
                    temp.setProfileId(profileId);
                    temp.setKey(ele.getKey());
                    temp.setValue(ele.getValue().toString());
                    propertyService.save(temp);
                } else {
                    temp.setValue(ele.getValue().toString());
                    propertyService.update(temp, "key", "value");
                }

            }
        });
        return Result.success().addData(data);
    }

    /**
     * 
     * @param id
     * @return
     */
    @GetMapping("delete/{id}")
    public Result delete(@PathVariable("id") long id) {
        return propertyService.delete(id) == 1 ? Result.success() : Result.fail("删除配置失败!");
    }

    /**
     * 
     * @param applicationId
     * @param profileId
     * @param labelId
     * @return
     */
    @GetMapping("{application}/{profile}/{label}")
    public Result config(@PathVariable("application") long applicationId, @PathVariable("profile") long profileId, @PathVariable("label") long labelId) {
        return Result.success()
                     .addData("properties",
                              propertyService.query(Cnd
                                                       .where("applicationId", "=	", applicationId)
                                                       .and("profileId", "=", profileId)
                                                       .and("labelId", "=", labelId)));
    }

    /**
     * 
     * @param applicationId
     * @return
     */
    @GetMapping("info/{applicationId}")
    public Result info(@PathVariable("applicationId") long applicationId) {
        Application application = applicationService.fetch(applicationId);
        List<User> members = userService.applicationMembers(applicationId);
        User manager = application == null ? null : userService.fetch(application.getOwnerId());
        return Result.success()
                     .addData("application", application)
                     .addData("members", members)
                     .addData("manager", manager)
                     .addData("profiles", profileService.query(Cnd.NEW().asc("weight")))
                     .addData("labels", labelService.query(Cnd.NEW().asc("weight")));
    }

    /**
     * 
     * @param applicationId
     * @return
     */
    @GetMapping("member/{applicationId}")
    public Result member(@PathVariable("applicationId") long applicationId) {
        return Result.success().addData("members", userService.selectableMembers(applicationId));
    }

    /**
     * 
     * @param applicationMember
     * @return
     */
    @PostMapping("member/add")
    public Result addMember(@RequestBody ApplicationMember applicationMember) {
        return applicationMemberService.save(applicationMember) == null ? Result.fail("添加项目成员失败") : Result.success();
    }

    /**
     * 
     * @param applicationMember
     * @return
     */
    @PostMapping("member/delete")
    public Result removeMember(@RequestBody ApplicationMember applicationMember) {
        return applicationMemberService.clear(Cnd.where("userId", "=", applicationMember.getUserId()).and("applicationId", "=", applicationMember.getApplicationId())) == 1
                                                                                                                                                                            ? Result.success()
                                                                                                                                                                            : Result.fail("移除项目成员失败");
    }

}
